
// 实现Promise静态方法resolve


const PENDING = "PENDING"
const FULLFILLED = "FULLFILLED"
const REJECTED = "REJECTED"

function MyPromise(flag, constructor){
    this.flag = flag
    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    this.onFullfilledCallbacks = new Array()
    this.onRejectedCallbacks = new Array()   

    const resolve = (value) =>{
       if(this.state === PENDING){
            this.state = FULLFILLED
            this.value = value
            this.onFullfilledCallbacks.forEach(fn => {
               fn()
            })
       }
    }

    const reject = (reason) => {
        if(this.state === PENDING){
            this.state = REJECTED
            this.reason = reason
            this.onRejectedCallbacks.forEach(fn => {
                fn()
            })
        }
    }

    try {
        constructor(resolve, reject)
    } catch (error) {
        reject(error)
    }
   
}

MyPromise.prototype.then = function(onFullfilled, onRejected) {
    onFullfilled = typeof onFullfilled === 'function'? onFullfilled : v => v
    onRejected = typeof onRejected === 'function'? onRejected : err => { throw err }
    
    let promise2 = new MyPromise(this.flag + 1, (resolve2, reject2)=>{
        if(this.state === PENDING){
            this.onFullfilledCallbacks.push(()=>{
               queueMicrotask(()=>{
                    let x = onFullfilled(this.value)
                    // 将上一个回调函数的执行结果 作为 下一个promise resolve的参数
                    resolvePromise(promise2, x, resolve2, reject2)
               })
            })
            this.onRejectedCallbacks.push(()=>{
                queueMicrotask(()=>{
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve2, reject2)
                    } catch (error) {
                        reject2(error)
                    }
                })
            })
        }else if(this.state === FULLFILLED){
           queueMicrotask(()=>{
                let x = onFullfilled(this.value)
                resolvePromise(promise2, x, resolve2, reject2)
           })
        }else if(this.state === REJECTED){
            queueMicrotask(()=>{
                let x  = onRejected(this.reason)
                resolvePromise(promise2, x, resolve2, reject2)
            })
        }
        
    })
    return promise2
}

const resolvePromise = function(promise2, x, resolve, reject){
    if(promise2 === x) new TypeError("Chaining cycle detected for promise")
    let called = false  
    if((typeof x === "object" && typeof x !== null) || typeof x === 'function'){
        try {
            let then = x.then 
            // 如果then是函数，就默认是promise了
            if(typeof then === 'function'){
                // 调用promise的then，第一个参数x是this, 需要传递成功回调函数 和 失败回调函数
                then.call(x, y => {
                    if(called) return
                    called = true
                    resolvePromise(promise2, y, resolve, reject)
                }, err => {
                    if(called) return
                    called = true
                    reject(err)
                })
            }else{
                resolve(x)
            }
        } catch (error) {
            reject(error)
        }
        
    }else{
        resolve(x)
    }
}

MyPromise.prototype.catch = function(onRejected){
   return this.then(null, onRejected )
}

/***
 * 1. finally 函数的参数是一个函数
 * 2. finally 函数也是异步微任务
 * 3. finally 没有参数
 * 4. finally 也有传递性
 */


MyPromise.resolve = function(x){
   let promise = new MyPromise(1,  (resolve, reject) => {
       queueMicrotask(()=>{
            resolvePromise(promise, x, resolve, reject)
       })
   })
   return promise
}

MyPromise.reject = function(x){
    return new MyPromise(1, (resolve, reject) => {
        reject(x)
    })
}

MyPromise.resolve(123).then((value)=>{
    console.log(value)
 })

 MyPromise.resolve(MyPromise.resolve(123)).then((value)=>{
     console.log(value)
 })


 MyPromise.resolve(MyPromise.reject(122)).then((value)=>{
    console.log("Promise then", value)
}).catch((reason) => {
    console.log("Promise catch", reason)
})


